local luv = require("luv")
local uuid = require("uuid")

--字符串分割函数
--传入字符串和分隔符，返回分割后的table
local function split(str, delimiter)
	if str==nil or str=='' or delimiter==nil then
		return nil
	end
    local result = {}
    for match in (str..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match)
    end
    return result
end
local ltrim = function (content)
	local rsl,_ = string.gsub(content, "^%s*(.*)$", "%1")
	return rsl
end
local rtrim = function (content)
	return string.reverse(ltrim(string.reverse(content)))
end
local trim = function (content)
	return rtrim(ltrim(content))
end

--数据流萃取器
--extract：提取方法，参数：待提取数据的trunk，返回值：提取数据后余下数据的trunk，如果返回的和传入的相同，就不再提取了
local StreamExtractor = function (extract)
    local data = ""
    local onTrunk = function (trunk)
        if not trunk then
            return
        end
        data = data .. trunk
        while true do
            local oldData = data
            local newData = extract(oldData)
            if oldData == newData then
                break
            else
                data = newData
            end
        end
    end
    return onTrunk
end

local parseHeader = function (headers, header)
    local group = split(header, ":")
    local key = trim(group[1])
    local val = group[2]
    headers[key] = val
end

local parseValueAndDetail = function (content)
    local group = split(content, ";")
    local data = {}
    if group then
        local value = trim(table.remove(group, 1))
        local detail = {}
        for index, item in ipairs(group) do
            local keyVal = split(item, "=")
            if keyVal then
                local key = trim(keyVal[1])
                local val = keyVal[2]
                detail[key] = val
            end
        end
        data.value = value
        data.detail = detail
    else
        data.value = content
    end
    return data
end

local isTextField = function (data)
    return data.value == "form-data" and data.detail and nil == data.detail.filename
end

local codeToStr = function (data)
    local str = nil
    pcall(function ()
        str = loadstring("return " .. data)()
    end)
    return str
end

--阶段枚举定义
local Stage = {
    BOUNDARY = 1, --边界阶段
    HEADER = 2, --分组头阶段
    BODY = 3, --分组体阶段
    FINISH = 4 --结束
}

local prefix="--"
local suffix="--"
local ONE_BREAK="\r\n"
local TWO_BREAK="\r\n\r\n"

local getUploadExtractor = function (boundary, fieldRecvHandler, storeEngine)
    local stage = Stage.BOUNDARY --初始化阶段
    local headers, body, contentDispositionData, isText, blobValue
    local extract = function (trunk)
        if Stage.BOUNDARY == stage then
            if string.len(trunk) < string.len(prefix) + string.len(boundary)+ string.len(suffix) then
                --trunk长度不够boundary结束标记长度，等长度够了再检查
                return trunk
            end
            --关闭模式查找，直接字符串查找，不然在ie下有性能问题，直接卡死
            local startIndex, endIndex = string.find(trunk, prefix .. boundary, 1, true)
            if startIndex then
                if 1 == startIndex then
                    --开始就是boundary
                    local breakIndex = string.find(trunk, prefix .. boundary .. suffix, 1, true)
                    if breakIndex == startIndex then
                        --整个请求结束
                        stage = Stage.FINISH
                        return string.sub(trunk, string.len(prefix) + string.len(boundary)+ string.len(suffix) + 1)
                    else
                        --请求未结束，还有内容
                        headers = {}
                        stage = Stage.HEADER
                        return string.sub(trunk, string.len(prefix) + string.len(boundary) + 1)
                    end
                else
                    --开始不是boundary，异常情况，忽略异常，继续处理
                    local addonTrunk = string.sub(trunk, 1, startIndex-1)
                    if isText then
                        body = body .. addonTrunk
                    else
                        if blobValue and "" ~= blobValue and body then
                            body.write(addonTrunk)
                        end
                    end
                    return string.sub(trunk, startIndex)
                end
            else
                --没找到，找到再处理
                return trunk
            end
        end
        if Stage.HEADER == stage then
            if string.len(trunk) < string.len(TWO_BREAK) then
                --trunk长度不够两个换行标记长度，等长度够了再检查
                return trunk
            end
            local startIndex, endIndex = string.find(trunk, ONE_BREAK, 1, true)
            if startIndex then
                if 1 == startIndex then
                    --开始就是换行符
                    local breakIndex = string.find(trunk, TWO_BREAK, 1, true)
                    if breakIndex == startIndex then
                        --开始就是2个换行符，请求头部分结束
                        contentDispositionData = parseValueAndDetail(headers["Content-Disposition"])
                        isText = isTextField(contentDispositionData)
                        if isText then
                            body = ""
                            blobValue = nil
                        else
                            blobValue = codeToStr(contentDispositionData.detail.filename)
                            if blobValue and "" ~= blobValue then
                                body = storeEngine.new()
                            else
                                body = nil
                            end
                        end
                        stage = Stage.BODY
                        return string.sub(trunk, string.len(TWO_BREAK)+1)
                    else
                        --还有请求头，跳过换行继续处理
                        return string.sub(trunk, string.len(ONE_BREAK)+1)
                    end
                else
                    --第1个不是换行符
                    local header = string.sub(trunk, 1, startIndex-1)
                    parseHeader(headers, header)
                    return string.sub(trunk, startIndex)
                end
            else
                --没找到换行符，找到再处理
                return trunk
            end
        end
        if Stage.BODY == stage then
            if string.len(trunk) < string.len(ONE_BREAK) + string.len(prefix) + string.len(boundary) then
                --trunk长度不够boundary标记长度，等长度够了再检查
                return trunk
            end
            local startIndex, endIndex = string.find(trunk, ONE_BREAK .. prefix .. boundary, 1, true)
            if startIndex then
                stage = Stage.BOUNDARY
                local remainData
                if 1 == startIndex then
                    --开始就是boundary，没有body的内容
                    remainData = string.sub(trunk, string.len(ONE_BREAK)+1)
                else
                    --开始不是boundary，提取body的内容
                    local addonTrunk = string.sub(trunk, 1, startIndex-1)
                    if isText then
                        body = body .. addonTrunk
                    else
                        if blobValue and "" ~= blobValue and body then
                            body.write(addonTrunk)
                            body.finish()
                        end
                    end
                    --暂时不能跳过边界，需要和接下来的内容合并一起判断是否结束边界
                    remainData = string.sub(trunk, startIndex + string.len(ONE_BREAK))
                end
                if isText then
                    --文本域
                    fieldRecvHandler(codeToStr(contentDispositionData.detail.name), body, true, headers)
                else
                    --文件域
                    local blobField = {
                        value = blobValue,
                        contentType = headers["Content-Type"],
                        blob = body
                    }
                    fieldRecvHandler(codeToStr(contentDispositionData.detail.name), blobField, false, headers)
                end
                return remainData
            else
                --没找到，提取body，留边界长度-1的内容在下一次处理
                local addonTrunk = string.sub(trunk, 1, -1*(string.len(ONE_BREAK) + string.len(prefix) + string.len(boundary)))
                if isText then
                    body = body .. addonTrunk
                else
                    if blobValue and "" ~= blobValue and body then
                        body.write(addonTrunk)
                    end
                end
                return string.sub(trunk, -1*(string.len(ONE_BREAK) + string.len(prefix) + string.len(boundary)-1))
            end
        end
        return trunk
    end
    return StreamExtractor(extract)
end

--检查请求是否是multipart/form-data格式，如果是，提取其boundary
local checkMultipart = function (req, fieldRecvHandler, storeEngine)
    local data = parseValueAndDetail(req.headers["Content-Type"])
    local isMultipart = ("multipart/form-data" == data.value)
    if isMultipart then
        return isMultipart, getUploadExtractor(data.detail.boundary, fieldRecvHandler, storeEngine)
    else
        return isMultipart
    end
end

local function genKey()
	uuid.seed()
	return uuid()
end

local function getFileStoreEngine(path)
    local engine = {}
    engine.new = function ()
        local fileName = genKey() .. ".blob"
        local filePath = path .. "/" .. fileName
        local file = assert(luv.fs_open(filePath, "w", 438))
        local writer = {}
        writer.path = filePath
        writer.write = function (chunk)
            luv.fs_write(file, chunk, -1)
        end
        writer.finish = function ()
            luv.fs_close(file)
        end
        writer.drop = function ()
            luv.fs_close(file)
            luv.fs_unlink(filePath)
        end
        return writer
    end
    return engine
end

local M = {}
M.checkMultipart = checkMultipart
M.getFileStoreEngine = getFileStoreEngine

local moduleName = ...
_G[moduleName] = M
complex = M
return complex